using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Threading;
namespace _050_dHID_LED_SW_VR
{
public partial class Form1 : Form
{
// Win32 API関数の呼び出し
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------
//Constant definitions from setupapi.h, which we aren't allowed to include directly since this is C#
internal const uint DIGCF_PRESENT = 0x02;
internal const uint DIGCF_DEVICEINTERFACE = 0x10;
//Constants for CreateFile() and other file I/O functions
internal const short FILE_ATTRIBUTE_NORMAL = 0x80;
internal const short INVALID_HANDLE_VALUE = -1;
internal const uint GENERIC_READ = 0x80000000;
internal const uint GENERIC_WRITE = 0x40000000;
internal const uint CREATE_NEW = 1;
internal const uint CREATE_ALWAYS = 2;
internal const uint OPEN_EXISTING = 3;
internal const uint FILE_SHARE_READ = 0x00000001;
internal const uint FILE_SHARE_WRITE = 0x00000002;
//Constant definitions for certain WM_DEVICECHANGE messages
internal const uint WM_DEVICECHANGE = 0x0219;
internal const uint DBT_DEVICEARRIVAL = 0x8000;
internal const uint DBT_DEVICEREMOVEPENDING = 0x8003;
internal const uint DBT_DEVICEREMOVECOMPLETE = 0x8004;
internal const uint DBT_CONFIGCHANGED = 0x0018;
//Other constant definitions
internal const uint DBT_DEVTYP_DEVICEINTERFACE = 0x05;
internal const uint DEVICE_NOTIFY_WINDOW_HANDLE = 0x00;
internal const uint ERROR_SUCCESS = 0x00;
internal const uint ERROR_NO_MORE_ITEMS = 0x00000103;
internal const uint SPDRP_HARDWAREID = 0x00000001;
//Various structure definitions for structures that this code will be using
internal struct SP_DEVICE_INTERFACE_DATA
{
internal uint cbSize; //DWORD
internal Guid InterfaceClassGuid; //GUID
internal uint Flags; //DWORD
internal uint Reserved; //ULONG_PTR MSDN says ULONG_PTR is "typedef unsigned __int3264 ULONG_PTR;"
}
internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
internal uint cbSize; //DWORD
internal char[] DevicePath; //TCHAR array of any size
}
internal struct SP_DEVINFO_DATA
{
internal uint cbSize; //DWORD
internal Guid ClassGuid; //GUID
internal uint DevInst; //DWORD
internal uint Reserved; //ULONG_PTR MSDN says ULONG_PTR is "typedef unsigned __int3264 ULONG_PTR;"
}
internal struct DEV_BROADCAST_DEVICEINTERFACE
{
internal uint dbcc_size; //DWORD
internal uint dbcc_devicetype; //DWORD
internal uint dbcc_reserved; //DWORD
internal Guid dbcc_classguid; //GUID
internal char[] dbcc_name; //TCHAR array
}
//DLL Imports. Need these to access various C style unmanaged functions contained in their respective DLL files.
//--------------------------------------------------------------------------------------------------------------
//Returns a HDEVINFO type for a device information set. We will need the
//HDEVINFO as in input parameter for calling many of the other SetupDixxx() functions.
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr SetupDiGetClassDevs(
ref Guid ClassGuid, //LPGUID Input: Need to supply the class GUID.
IntPtr Enumerator, //PCTSTR Input: Use NULL here, not important for our purposes
IntPtr hwndParent, //HWND Input: Use NULL here, not important for our purposes
uint Flags); //DWORD Input: Flags describing what kind of filtering to use.
//Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different
//from class GUID). We need the interface GUID to get the device path.
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiEnumDeviceInterfaces(
IntPtr DeviceInfoSet, //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs()
IntPtr DeviceInfoData, //Input (optional)
ref Guid InterfaceClassGuid, //Input
uint MemberIndex, //Input: "Index" of the device you are interested in getting the path for.
ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); //Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure.
//SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiDestroyDeviceInfoList(
IntPtr DeviceInfoSet); //Input: Give it a handle to a device info list to deallocate from RAM.
//SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty()
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiEnumDeviceInfo(
IntPtr DeviceInfoSet,
uint MemberIndex,
ref SP_DEVINFO_DATA DeviceInterfaceData);
//SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiGetDeviceRegistryProperty(
IntPtr DeviceInfoSet,
ref SP_DEVINFO_DATA DeviceInfoData,
uint Property,
ref uint PropertyRegDataType,
IntPtr PropertyBuffer,
uint PropertyBufferSize,
ref uint RequiredSize);
//SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used.
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiGetDeviceInterfaceDetail(
IntPtr DeviceInfoSet, //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, //Input: Pointer to an structure which defines the device interface.
IntPtr DeviceInterfaceDetailData, //Output: Pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA structure, which will receive the device path.
uint DeviceInterfaceDetailDataSize, //Input: Number of bytes to retrieve.
ref uint RequiredSize, //Output (optional): The number of bytes needed to hold the entire struct
IntPtr DeviceInfoData); //Output (optional): Pointer to a SP_DEVINFO_DATA structure
//Overload for SetupDiGetDeviceInterfaceDetail(). Need this one since we can't pass NULL pointers directly in C#.
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiGetDeviceInterfaceDetail(
IntPtr DeviceInfoSet, //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, //Input: Pointer to an structure which defines the device interface.
IntPtr DeviceInterfaceDetailData, //Output: Pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA structure, which will contain the device path.
uint DeviceInterfaceDetailDataSize, //Input: Number of bytes to retrieve.
IntPtr RequiredSize, //Output (optional): Pointer to a DWORD to tell you the number of bytes needed to hold the entire struct
IntPtr DeviceInfoData); //Output (optional): Pointer to a SP_DEVINFO_DATA structure
//Need this function for receiving all of the WM_DEVICECHANGE messages. See MSDN documentation for
//description of what this function does/how to use it. Note: name is remapped "RegisterDeviceNotificationUM" to
//avoid possible build error conflicts.
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr RegisterDeviceNotification(
IntPtr hRecipient,
IntPtr NotificationFilter,
uint Flags);
//Takes in a device path and opens a handle to the device.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
//Uses a handle (created with CreateFile()), and lets us write USB data to the device.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool WriteFile(
SafeFileHandle hFile,
byte[] lpBuffer,
uint nNumberOfBytesToWrite,
ref uint lpNumberOfBytesWritten,
IntPtr lpOverlapped);
//Uses a handle (created with CreateFile()), and lets us read USB data from the device.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool ReadFile(
SafeFileHandle hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToRead,
ref uint lpNumberOfBytesRead,
IntPtr lpOverlapped);
//グローバル変数の定義
//--------------- Global Varibles Section ------------------
//USB related variables that need to have wide scope.
bool AttachedState = false; //Need to keep track of the USB device attachment status for proper plug and play operation.
bool AttachedButBroken = false;
SafeFileHandle WriteHandleToUSBDevice = null;
SafeFileHandle ReadHandleToUSBDevice = null;
String DevicePath = null; //Need the find the proper device path before you can open file handles.
//Variables used by the application/form updates.
bool LedBtnClicked = false; //LEDボタン クリック
bool StateChecked = false; //スイッチ、ADコンバータ入力電圧状態チェック
bool LedMode = false;
bool SwMode = false;
//Globally Unique Identifier (GUID) for HID class devices. Windows uses GUIDs to identify things.
Guid InterfaceClassGuid = new Guid(0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
//--------------- End of Global Varibles ------------------
//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
public Form1()
{
InitializeComponent();
groupBox1.Visible = false;
groupBox2.Visible = false;
groupBox3.Visible = false;
OnLed_pictureBox.Visible = false; //Led オン画像非表示
OffLed_pictureBox.Visible = true; //Led オフ画像表示
OnSw_pictureBox.Visible = false; //SW オン画像非表示
OffSw_pictureBox.Visible = true; //SW オフ画像表示
VR_progressBar.Value = 0;
//接続状態表示ランプの表示
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50);//灰色塗りつぶし 基点(10,0) 幅×高さ:50×50
}
private void USB_ConnectBtn_Click(object sender, EventArgs e)
{
try
{
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------
//Additional constructor code
//Initialize tool tips, to provide pop up help when the mouse cursor is moved over objects on the form.
//ys
//ANxVoltageToolTip.SetToolTip(this.ANxVoltage_lbl, "If using a board/PIM without a potentiometer, apply an adjustable voltage to the I/O pin.");
//ANxVoltageToolTip.SetToolTip(this.progressBar1, "If using a board/PIM without a potentiometer, apply an adjustable voltage to the I/O pin.");
//ToggleLEDToolTip.SetToolTip(this.ToggleLEDs_btn, "Sends a packet of data to the USB device.");
//PushbuttonStateTooltip.SetToolTip(this.PushbuttonState_lbl, "Try pressing pushbuttons on the USB demo board/PIM.");
//ys
//Register for WM_DEVICECHANGE notifications. This code uses these messages to detect plug and play connection/disconnection events for USB devices
DEV_BROADCAST_DEVICEINTERFACE DeviceBroadcastHeader = new DEV_BROADCAST_DEVICEINTERFACE();
DeviceBroadcastHeader.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
DeviceBroadcastHeader.dbcc_size = (uint)Marshal.SizeOf(DeviceBroadcastHeader);
DeviceBroadcastHeader.dbcc_reserved = 0; //Reserved says not to use...
DeviceBroadcastHeader.dbcc_classguid = InterfaceClassGuid;
//Need to get the address of the DeviceBroadcastHeader to call RegisterDeviceNotification(), but
//can't use "&DeviceBroadcastHeader". Instead, using a roundabout means to get the address by
//making a duplicate copy using Marshal.StructureToPtr().
IntPtr pDeviceBroadcastHeader = IntPtr.Zero; //Make a pointer.
pDeviceBroadcastHeader = Marshal.AllocHGlobal(Marshal.SizeOf(DeviceBroadcastHeader)); //allocate memory for a new DEV_BROADCAST_DEVICEINTERFACE structure, and return the address
Marshal.StructureToPtr(DeviceBroadcastHeader, pDeviceBroadcastHeader, false); //Copies the DeviceBroadcastHeader structure into the memory already allocated at DeviceBroadcastHeaderWithPointer
RegisterDeviceNotification(this.Handle, pDeviceBroadcastHeader, DEVICE_NOTIFY_WINDOW_HANDLE);
//Now make an initial attempt to find the USB device, if it was already connected to the PC and enumerated prior to launching the application.
//If it is connected and present, we should open read and write handles to the device so we can communicate with it later.
//If it was not connected, we will have to wait until the user plugs the device in, and the WM_DEVICECHANGE callback function can process
//the message and again search for the device.
if (CheckIfPresentAndGetUSBDevicePath()) //Check and make sure at least one device with matching VID/PID is attached
{
uint ErrorStatusWrite;
uint ErrorStatusRead;
//We now have the proper device path, and we can finally open read and write handles to the device.
WriteHandleToUSBDevice = CreateFile(DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
ErrorStatusWrite = (uint)Marshal.GetLastWin32Error();
ReadHandleToUSBDevice = CreateFile(DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
ErrorStatusRead = (uint)Marshal.GetLastWin32Error();
if ((ErrorStatusWrite == ERROR_SUCCESS) && (ErrorStatusRead == ERROR_SUCCESS))
{
AttachedState = true; //Let the rest of the PC application know the USB device is connected, and it is safe to read/write to it
AttachedButBroken = false;
//StatusBox_txtbx.Text = "my1_Device Found, AttachedState = TRUE";
}
else //for some reason the device was physically plugged in, but one or both of the read/write handles didn't open successfully...
{
AttachedState = false; //Let the rest of this application known not to read/write to the device.
AttachedButBroken = true; //Flag so that next time a WM_DEVICECHANGE message occurs, can retry to re-open read/write pipes
if (ErrorStatusWrite == ERROR_SUCCESS)
WriteHandleToUSBDevice.Close();
if (ErrorStatusRead == ERROR_SUCCESS)
ReadHandleToUSBDevice.Close();
}
}
else //Device must not be connected (or not programmed with correct firmware)
{
AttachedState = false;
AttachedButBroken = false;
}
if (AttachedState == true)
{
ConnectLabel.Text = "接続完了";
//接続状態表示ランプの表示色 → 橙色
groupBox1.Visible = true;
groupBox2.Visible = true;
groupBox3.Visible = true;
OnLed_pictureBox.Visible = false; //Led オン画像非表示
OffLed_pictureBox.Visible = true; //Led オフ画像表示
OnSw_pictureBox.Visible = false; //SW オン画像非表示
OffSw_pictureBox.Visible = true; //SW オフ画像表示
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Orange, 10, 0, 50, 50);//橙色塗りつぶし 基点(10,0) 幅×高さ:50×50
timer1.Enabled = true;
LedBtnClicked = false; //LEDボタン クリック
StateChecked = false; //スイッチ状態チェック
LedMode = false;
SwMode = false;
}
else
{
//StatusBox_txtbx.Text = "Device not found, verify connect/correct firmware";
}
ReadWriteThread.RunWorkerAsync(); //Recommend performing USB read/write operations in a separate thread. Otherwise,
//the Read/Write operations are effectively blocking functions and can lock up the
//user interface if the I/O operations take a long time to complete.
//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
}
catch
{
}
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------
//FUNCTION: CheckIfPresentAndGetUSBDevicePath()
//PURPOSE: Check if a USB device is currently plugged in with a matching VID and PID
//INPUT: Uses globally declared String DevicePath, globally declared GUID, and the MY_DEVICE_ID constant.
//OUTPUT: Returns BOOL. TRUE when device with matching VID/PID found. FALSE if device with VID/PID could not be found.
// When returns TRUE, the globally accessable "DetailedInterfaceDataStructure" will contain the device path
// to the USB device with the matching VID/PID.
bool CheckIfPresentAndGetUSBDevicePath()
{
/*
Before we can "connect" our application to our USB embedded device, we must first find the device.
A USB bus can have many devices simultaneously connected, so somehow we have to find our device only.
This is done with the Vendor ID (VID) and Product ID (PID). Each USB product line should have
a unique combination of VID and PID.
Microsoft has created a number of functions which are useful for finding plug and play devices. Documentation
for each function used can be found in the MSDN library. We will be using the following functions (unmanaged C functions):
SetupDiGetClassDevs() //provided by setupapi.dll, which comes with Windows
SetupDiEnumDeviceInterfaces() //provided by setupapi.dll, which comes with Windows
GetLastError() //provided by kernel32.dll, which comes with Windows
SetupDiDestroyDeviceInfoList() //provided by setupapi.dll, which comes with Windows
SetupDiGetDeviceInterfaceDetail() //provided by setupapi.dll, which comes with Windows
SetupDiGetDeviceRegistryProperty() //provided by setupapi.dll, which comes with Windows
CreateFile() //provided by kernel32.dll, which comes with Windows
In order to call these unmanaged functions, the Marshal class is very useful.
We will also be using the following unusual data types and structures. Documentation can also be found in
the MSDN library:
PSP_DEVICE_INTERFACE_DATA
PSP_DEVICE_INTERFACE_DETAIL_DATA
SP_DEVINFO_DATA
HDEVINFO
HANDLE
GUID
The ultimate objective of the following code is to get the device path, which will be used elsewhere for getting
read and write handles to the USB device. Once the read/write handles are opened, only then can this
PC application begin reading/writing to the USB device using the WriteFile() and ReadFile() functions.
Getting the device path is a multi-step round about process, which requires calling several of the
SetupDixxx() functions provided by setupapi.dll.
*/
try
{
IntPtr DeviceInfoTable = IntPtr.Zero;
SP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA();
SP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure = new SP_DEVICE_INTERFACE_DETAIL_DATA();
SP_DEVINFO_DATA DevInfoData = new SP_DEVINFO_DATA();
uint InterfaceIndex = 0;
uint dwRegType = 0;
uint dwRegSize = 0;
uint dwRegSize2 = 0;
uint StructureSize = 0;
IntPtr PropertyValueBuffer = IntPtr.Zero;
bool MatchFound = false;
uint ErrorStatus;
uint LoopCounter = 0;
//Use the formatting: "Vid_xxxx&Pid_xxxx" where xxxx is a 16-bit hexadecimal number.
//Make sure the value appearing in the parathesis matches the USB device descriptor
//of the device that this aplication is intending to find.
String DeviceIDToFind = "Vid_04d8&Pid_003f";
//First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID.
DeviceInfoTable = SetupDiGetClassDevs(ref InterfaceClassGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (DeviceInfoTable != IntPtr.Zero)
{
//Now look through the list we just populated. We are trying to see if any of them match our device.
while (true)
{
InterfaceDataStructure.cbSize = (uint)Marshal.SizeOf(InterfaceDataStructure);
if (SetupDiEnumDeviceInterfaces(DeviceInfoTable, IntPtr.Zero, ref InterfaceClassGuid, InterfaceIndex, ref InterfaceDataStructure))
{
ErrorStatus = (uint)Marshal.GetLastWin32Error();
if (ErrorStatus == ERROR_NO_MORE_ITEMS) //Did we reach the end of the list of matching devices in the DeviceInfoTable?
{ //Cound not find the device. Must not have been attached.
SetupDiDestroyDeviceInfoList(DeviceInfoTable); //Clean up the old structure we no longer need.
return false;
}
}
else //Else some other kind of unknown error ocurred...
{
ErrorStatus = (uint)Marshal.GetLastWin32Error();
SetupDiDestroyDeviceInfoList(DeviceInfoTable); //Clean up the old structure we no longer need.
return false;
}
//Now retrieve the hardware ID from the registry. The hardware ID contains the VID and PID, which we will then
//check to see if it is the correct device or not.
//Initialize an appropriate SP_DEVINFO_DATA structure. We need this structure for SetupDiGetDeviceRegistryProperty().
DevInfoData.cbSize = (uint)Marshal.SizeOf(DevInfoData);
SetupDiEnumDeviceInfo(DeviceInfoTable, InterfaceIndex, ref DevInfoData);
//First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data.
SetupDiGetDeviceRegistryProperty(DeviceInfoTable, ref DevInfoData, SPDRP_HARDWAREID, ref dwRegType, IntPtr.Zero, 0, ref dwRegSize);
//Allocate a buffer for the hardware ID.
//Should normally work, but could throw exception "OutOfMemoryException" if not enough resources available.
PropertyValueBuffer = Marshal.AllocHGlobal((int)dwRegSize);
//Retrieve the hardware IDs for the current device we are looking at. PropertyValueBuffer gets filled with a
//REG_MULTI_SZ (array of null terminated strings). To find a device, we only care about the very first string in the
//buffer, which will be the "device ID". The device ID is a string which contains the VID and PID, in the example
//format "Vid_04d8&Pid_003f".
SetupDiGetDeviceRegistryProperty(DeviceInfoTable, ref DevInfoData, SPDRP_HARDWAREID, ref dwRegType, PropertyValueBuffer, dwRegSize, ref dwRegSize2);
//Now check if the first string in the hardware ID matches the device ID of the USB device we are trying to find.
String DeviceIDFromRegistry = Marshal.PtrToStringUni(PropertyValueBuffer); //Make a new string, fill it with the contents from the PropertyValueBuffer
Marshal.FreeHGlobal(PropertyValueBuffer); //No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks
//Convert both strings to lower case. This makes the code more robust/portable accross OS Versions
DeviceIDFromRegistry = DeviceIDFromRegistry.ToLowerInvariant();
DeviceIDToFind = DeviceIDToFind.ToLowerInvariant();
//Now check if the hardware ID we are looking at contains the correct VID/PID
MatchFound = DeviceIDFromRegistry.Contains(DeviceIDToFind);
if (MatchFound == true)
{
//Device must have been found. In order to open I/O file handle(s), we will need the actual device path first.
//We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice: The first
//time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually
//get the structure (after we have allocated enough memory for the structure.)
DetailedInterfaceDataStructure.cbSize = (uint)Marshal.SizeOf(DetailedInterfaceDataStructure);
//First call populates "StructureSize" with the correct value
SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, ref InterfaceDataStructure, IntPtr.Zero, 0, ref StructureSize, IntPtr.Zero);
//Need to call SetupDiGetDeviceInterfaceDetail() again, this time specifying a pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA buffer with the correct size of RAM allocated.
//First need to allocate the unmanaged buffer and get a pointer to it.
IntPtr pUnmanagedDetailedInterfaceDataStructure = IntPtr.Zero; //Declare a pointer.
pUnmanagedDetailedInterfaceDataStructure = Marshal.AllocHGlobal((int)StructureSize); //Reserve some unmanaged memory for the structure.
DetailedInterfaceDataStructure.cbSize = 6; //Initialize the cbSize parameter (4 bytes for DWORD + 2 bytes for unicode null terminator)
Marshal.StructureToPtr(DetailedInterfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, false); //Copy managed structure contents into the unmanaged memory buffer.
//Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the device path in the structure at pUnmanagedDetailedInterfaceDataStructure.
if (SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, ref InterfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, StructureSize, IntPtr.Zero, IntPtr.Zero))
{
//Need to extract the path information from the unmanaged "structure". The path starts at (pUnmanagedDetailedInterfaceDataStructure + sizeof(DWORD)).
IntPtr pToDevicePath = new IntPtr((uint)pUnmanagedDetailedInterfaceDataStructure.ToInt32() + 4); //Add 4 to the pointer (to get the pointer to point to the path, instead of the DWORD cbSize parameter)
DevicePath = Marshal.PtrToStringUni(pToDevicePath); //Now copy the path information into the globally defined DevicePath String.
//We now have the proper device path, and we can finally use the path to open I/O handle(s) to the device.
SetupDiDestroyDeviceInfoList(DeviceInfoTable); //Clean up the old structure we no longer need.
Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure); //No longer need this unmanaged SP_DEVICE_INTERFACE_DETAIL_DATA buffer. We already extracted the path information.
return true; //Returning the device path in the global DevicePath String
}
else //Some unknown failure occurred
{
uint ErrorCode = (uint)Marshal.GetLastWin32Error();
SetupDiDestroyDeviceInfoList(DeviceInfoTable); //Clean up the old structure.
Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure); //No longer need this unmanaged SP_DEVICE_INTERFACE_DETAIL_DATA buffer. We already extracted the path information.
return false;
}
}
InterfaceIndex++;
//Keep looping until we either find a device with matching VID and PID, or until we run out of devices to check.
//However, just in case some unexpected error occurs, keep track of the number of loops executed.
//If the number of loops exceeds a very large number, exit anyway, to prevent inadvertent infinite looping.
LoopCounter++;
if (LoopCounter == 10000000) //Surely there aren't more than 10 million devices attached to any forseeable PC...
{
return false;
}
}//end of while(true)
}
return false;
}//end of try
catch
{
//Something went wrong if PC gets here. Maybe a Marshal.AllocHGlobal() failed due to insufficient resources or something.
return false;
}
}
//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------
//This is a callback function that gets called when a Windows message is received by the form.
//We will receive various different types of messages, but the ones we really want to use are the WM_DEVICECHANGE messages.
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE)
{
if (((int)m.WParam == DBT_DEVICEARRIVAL) || ((int)m.WParam == DBT_DEVICEREMOVEPENDING) || ((int)m.WParam == DBT_DEVICEREMOVECOMPLETE) || ((int)m.WParam == DBT_CONFIGCHANGED))
{
//WM_DEVICECHANGE messages by themselves are quite generic, and can be caused by a number of different
//sources, not just your USB hardware device. Therefore, must check to find out if any changes relavant
//to your device (with known VID/PID) took place before doing any kind of opening or closing of handles/endpoints.
//(the message could have been totally unrelated to your application/USB device)
if (CheckIfPresentAndGetUSBDevicePath()) //Check and make sure at least one device with matching VID/PID is attached
{
//If executes to here, this means the device is currently attached and was found.
//This code needs to decide however what to do, based on whether or not the device was previously known to be
//attached or not.
if ((AttachedState == false) || (AttachedButBroken == true)) //Check the previous attachment state
{
uint ErrorStatusWrite;
uint ErrorStatusRead;
//We obtained the proper device path (from CheckIfPresentAndGetUSBDevicePath() function call), and it
//is now possible to open read and write handles to the device.
WriteHandleToUSBDevice = CreateFile(DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
ErrorStatusWrite = (uint)Marshal.GetLastWin32Error();
ReadHandleToUSBDevice = CreateFile(DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
ErrorStatusRead = (uint)Marshal.GetLastWin32Error();
if ((ErrorStatusWrite == ERROR_SUCCESS) && (ErrorStatusRead == ERROR_SUCCESS))
{
AttachedState = true; //Let the rest of the PC application know the USB device is connected, and it is safe to read/write to it
AttachedButBroken = false;
}
else //for some reason the device was physically plugged in, but one or both of the read/write handles didn't open successfully...
{
AttachedState = false; //Let the rest of this application known not to read/write to the device.
AttachedButBroken = true; //Flag so that next time a WM_DEVICECHANGE message occurs, can retry to re-open read/write pipes
if (ErrorStatusWrite == ERROR_SUCCESS)
WriteHandleToUSBDevice.Close();
if (ErrorStatusRead == ERROR_SUCCESS)
ReadHandleToUSBDevice.Close();
}
}
//else we did find the device, but AttachedState was already true. In this case, don't do anything to the read/write handles,
//since the WM_DEVICECHANGE message presumably wasn't caused by our USB device.
}
else //Device must not be connected (or not programmed with correct firmware)
{
if (AttachedState == true) //If it is currently set to true, that means the device was just now disconnected
{
AttachedState = false;
WriteHandleToUSBDevice.Close();
ReadHandleToUSBDevice.Close();
}
AttachedState = false;
AttachedButBroken = false;
}
}
} //end of: if(m.Msg == WM_DEVICECHANGE)
if((m.Msg == WM_DEVICECHANGE) && ((int)m.WParam == DBT_DEVICEREMOVECOMPLETE))
{
ConnectLabel.Text = "未接続";
groupBox1.Visible = false;
groupBox2.Visible = false;
groupBox3.Visible = false;
OnLed_pictureBox.Visible = false; //Led オン画像非表示
OffLed_pictureBox.Visible = false; //Led オフ画像表示
OnSw_pictureBox.Visible = false; //SW オン画像非表示
OffSw_pictureBox.Visible = false; //SW オフ画像表示
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50);//灰色塗りつぶし 基点(10,0) 幅×高さ:50×50
timer1.Enabled = false;
ReadWriteThread.CancelAsync();
}
base.WndProc(ref m);
} //end of: WndProc() function
//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
private void ReadWriteThread_DoWork(object sender, DoWorkEventArgs e)
{
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------
/*This thread does the actual USB read/write operations (but only when AttachedState == true) to the USB device.
It is generally preferrable to write applications so that read and write operations are handled in a separate
thread from the main form. This makes it so that the main form can remain responsive, even if the I/O operations
take a very long time to complete.
Since this is a separate thread, this code below executes independently from the rest of the
code in this application. All this thread does is read and write to the USB device. It does not update
the form directly with the new information it obtains (such as the ANxx/POT Voltage or pushbutton state).
The information that this thread obtains is stored in atomic global variables.
Form updates are handled by the FormUpdateTimer Tick event handler function.
This application sends packets to the endpoint buffer on the USB device by using the "WriteFile()" function.
This application receives packets from the endpoint buffer on the USB device by using the "ReadFile()" function.
Both of these functions are documented in the MSDN library. Calling ReadFile() is a not perfectly straight
foward in C# environment, since one of the input parameters is a pointer to a buffer that gets filled by ReadFile().
The ReadFile() function is therefore called through a wrapper function ReadFileManagedBuffer().
All ReadFile() and WriteFile() operations in this example project are synchronous. They are blocking function
calls and only return when they are complete, or if they fail because of some event, such as the user unplugging
the device. It is possible to call these functions with "overlapped" structures, and use them as non-blocking
asynchronous I/O function calls.
Note: This code may perform differently on some machines when the USB device is plugged into the host through a
USB 2.0 hub, as opposed to a direct connection to a root port on the PC. In some cases the data rate may be slower
when the device is connected through a USB 2.0 hub. This performance difference is believed to be caused by
the issue described in Microsoft knowledge base article 940021:
http://support.microsoft.com/kb/940021/en-us
Higher effective bandwidth (up to the maximum offered by interrupt endpoints), both when connected
directly and through a USB 2.0 hub, can generally be achieved by queuing up multiple pending read and/or
write requests simultaneously. This can be done when using asynchronous I/O operations (calling ReadFile() and
WriteFile() with overlapped structures). The Microchip HID USB Bootloader application uses asynchronous I/O
for some USB operations and the source code can be used as an example.*/
Byte[] OUTBuffer = new byte[65]; //Allocate a memory buffer equal to the OUT endpoint size + 1
Byte[] INBuffer = new byte[65]; //Allocate a memory buffer equal to the IN endpoint size + 1
uint BytesWritten = 0;
uint BytesRead = 0;
bool LedMode2;
uint ADCValue;
while (true)
{
try
{
if (AttachedState == true) //Do not try to use the read/write handles unless the USB device is attached and ready
{
//// 接続確認確認
//OUTBuffer[0] = 0; // Report ID
//OUTBuffer[1] = 0x30; // 接続確認
//// 送信
//WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
////受信
//ReadFileManagedBuffer(ReadHandleToUSBDevice, INBuffer, 65, ref BytesRead, IntPtr.Zero);
//if (!((INBuffer[3] == 'R') && //'R' 'e' 'a' 'd' 'y' 文字を受信できない場合
// (INBuffer[4] == 'e') &&
// (INBuffer[5] == 'a') &&
// (INBuffer[6] == 'd') &&
// (INBuffer[7] == 'y')))
//{
// NoArgDelegate nrd = new NoArgDelegate(ConnectAlarm); //デリゲートのインスタンス生成、初期化
// this.Invoke(nrd); //デリゲート関数呼び出し //this.Invoke(デリゲートのインスタンス, 第1引数);
//}
//LED 点灯/消灯
if (LedBtnClicked == true)
{
OUTBuffer[0] = 0; // Report ID
OUTBuffer[1] = 0x80; // LED制御コマンド
OUTBuffer[2] = 0x30; // LEDアドレス
if (LedMode == false) //消灯していたなら
{
LedMode = true;
OUTBuffer[3] = 0x31; // Led点灯 //0x31 = '1'
}
else
{
LedMode = false;
OUTBuffer[3] = 0x30; // Led消灯 //0x30 = '0'
}
// 送信
WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
//受信
ReadFileManagedBuffer(ReadHandleToUSBDevice, INBuffer, 65, ref BytesRead, IntPtr.Zero);
if(INBuffer[3] == 0x31) //点灯
{
LedMode2 = true;
}
else
{
LedMode2 = false;
}
LedBtnClicked = false;
LedStatusDelegate lsd = new LedStatusDelegate(LedMonitor); //デリゲートのインスタンス生成、初期化
this.Invoke(lsd, LedMode2); //デリゲート関数呼び出し //this.Invoke(デリゲートのインスタンス, 第1引数);
}
//スイッチの状態読み出し
if (StateChecked == true)
{
//スイッチの状態読込
OUTBuffer[0] = 0; // Report ID
OUTBuffer[1] = 0x81; // SW状態要求コマンド
OUTBuffer[2] = 0x31; // SW1
// 送信
WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
//受信
ReadFileManagedBuffer(ReadHandleToUSBDevice, INBuffer, 65, ref BytesRead, IntPtr.Zero);
if (INBuffer[3] == 0x30) SwMode = true; //SW: ON
else SwMode = false; //SW:OFF
SwStatusDelegate ssd = new SwStatusDelegate(SwMonitor); //デリゲートのインスタンス生成、初期化
this.Invoke(ssd, SwMode); //デリゲート関数呼び出し //this.Invoke(デリゲートのインスタンス, 第1引数);
//可変抵抗器出力電圧のAD変換値 読込
OUTBuffer[0] = 0; // Report ID
OUTBuffer[1] = 0x82; // 可変抵抗器outのAD変換値読込
OUTBuffer[2] = 0x31; // VR1
// 送信
WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
//受信
ReadFileManagedBuffer(ReadHandleToUSBDevice, INBuffer, 65, ref BytesRead, IntPtr.Zero);
ADCValue = (uint)(INBuffer[4] * 256) + INBuffer[3];
ADCStatusDelegate lsd = new ADCStatusDelegate(ADCMonitor); //デリゲートのインスタンス生成、初期化
this.Invoke(lsd, ADCValue); //デリゲート関数呼び出し //this.Invoke(デリゲートのインスタンス, 第1引数);
StateChecked = false;
}
}
}
catch
{
//Exceptions can occur during the read or write operations. For example,
//exceptions may occur if for instance the USB device is physically unplugged
//from the host while the above read/write functions are executing.
//Don't need to do anything special in this case. The application will automatically
//re-establish communications based on the global AttachedState boolean variable used
//in conjunction with the WM_DEVICECHANGE messages to dyanmically respond to Plug and Play
//USB connection events.
}
} //end of while(true) loop
//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------
//FUNCTION: ReadFileManagedBuffer()
//PURPOSE: Wrapper function to call ReadFile()
//
//INPUT: Uses managed versions of the same input parameters as ReadFile() uses.
//
//OUTPUT: Returns boolean indicating if the function call was successful or not.
// Also returns data in the byte[] INBuffer, and the number of bytes read.
//
//Notes: Wrapper function used to call the ReadFile() function. ReadFile() takes a pointer to an unmanaged buffer and deposits
// the bytes read into the buffer. However, can't pass a pointer to a managed buffer directly to ReadFile().
// This ReadFileManagedBuffer() is a wrapper function to make it so application code can call ReadFile() easier
// by specifying a managed buffer.
//--------------------------------------------------------------------------------------------------------------------------
public unsafe bool ReadFileManagedBuffer(SafeFileHandle hFile, byte[] INBuffer, uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, IntPtr lpOverlapped)
{
IntPtr pINBuffer = IntPtr.Zero;
try
{
pINBuffer = Marshal.AllocHGlobal((int)nNumberOfBytesToRead); //Allocate some unmanged RAM for the receive data buffer.
if (ReadFile(hFile, pINBuffer, nNumberOfBytesToRead, ref lpNumberOfBytesRead, lpOverlapped))
{
Marshal.Copy(pINBuffer, INBuffer, 0, (int)lpNumberOfBytesRead); //Copy over the data from unmanged memory into the managed byte[] INBuffer
Marshal.FreeHGlobal(pINBuffer);
return true;
}
else
{
Marshal.FreeHGlobal(pINBuffer);
return false;
}
}
catch
{
if (pINBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(pINBuffer);
}
return false;
}
}
//-------------------------------------------------------------------------------------------------------
private void LedBtn_Click(object sender, EventArgs e)
{
LedBtnClicked = true;
}
//デリゲートの型を定義
delegate void LedStatusDelegate(bool arg1);
private void LedMonitor(bool LedMode) //LEDの状態をモニタ //デリゲート用の関数を定義
{
if (LedMode == true) //LED点灯の場合
{
OnLed_pictureBox.Visible = true; //ランプ点灯のピクチャボックス →表示
OffLed_pictureBox.Visible = false; //ランプ消灯のピクチャボックス →非表示
LedBtn.BackColor = Color.Red; //LED ON/OFFボタンの背景色 →赤色
}
else //LED消灯の場合
{
OnLed_pictureBox.Visible = false;
OffLed_pictureBox.Visible = true;
LedBtn.BackColor = Color.Gray;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
StateChecked = true;
}
//デリゲートの型を定義
delegate void SwStatusDelegate(bool arg1);
private void SwMonitor(bool SwMode)
{
if(SwMode == true)
{
OnSw_pictureBox.Visible = true;
OffSw_pictureBox.Visible = false;
pictureBox2.Image = new Bitmap(pictureBox2.Width, pictureBox2.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox2.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Orange, 10, 0, 50, 50);//橙色塗りつぶし 基点(10,0) 幅×高さ:50×50
}
else
{
OnSw_pictureBox.Visible = false;
OffSw_pictureBox.Visible = true;
pictureBox2.Image = new Bitmap(pictureBox2.Width, pictureBox2.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox2.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50);//灰色塗りつぶし 基点(10,0) 幅×高さ:50×50
}
}
//デリゲートの型を定義
delegate void ADCStatusDelegate(uint arg1);
private void ADCMonitor(uint adcValue) //LEDの状態をモニタ //デリゲート用の関数を定義
{
int temp;
double Volt;
Volt = (adcValue * 3.3) / 4096;
VR_textBox.Text = string.Format("{0:f2} [V]", Volt); //小数点以下2桁にして表示
//四捨五入
temp = (int)((((float)adcValue * 100) / 4096) + 0.5); //0.5を足して、小数点以下を切り捨てる
VR_progressBar.Value = temp;
}
//デリゲートの型を定義
delegate void NoArgDelegate();
private void ConnectAlarm() //LEDの状態をモニタ //デリゲート用の関数を定義
{
ConnectLabel.Text = "未接続";
groupBox1.Visible = false;
groupBox2.Visible = false;
groupBox3.Visible = false;
OnLed_pictureBox.Visible = false; //Led オン画像非表示
OffLed_pictureBox.Visible = false; //Led オフ画像表示
OnSw_pictureBox.Visible = false; //SW オン画像非表示
OffSw_pictureBox.Visible = false; //SW オフ画像表示
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50);//灰色塗りつぶし 基点(10,0) 幅×高さ:50×50
timer1.Enabled = false;
}
}
}
|